import { _decorator, Component, EventMouse, Node, renderer, Sprite, tween, UITransform, v2, v3, Vec2 } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('Fake3DCtrl')
export class Fake3DCtrl extends Component {
    @property minAngle: number = -25;
    @property maxAngle: number = 25;

    private pass: renderer.Pass = null;
    private y_rot: number = 0;
    private x_rot: number = 0;

    private spriteWidth: number = 0;
    private spriteHeight: number = 0;

    private lastYRot: number = 0;
    private lastXRot: number = 0;

    start(): void {
        // 材质 初始化
        const sprite = this.node.getComponent(Sprite);
        this.spriteWidth = sprite.spriteFrame.width;
        this.spriteHeight = sprite.spriteFrame.height;

        const material = sprite.getMaterialInstance(0);
        material.setProperty('textureSize', v2(this.spriteWidth , this.spriteHeight));

        this.pass = material.passes[0];
        this.y_rot = this.pass.getHandle('y_rot');
        this.x_rot = this.pass.getHandle('x_rot');

        // 监听鼠标移动
        this.node.on(Node.EventType.MOUSE_ENTER, this.onMouseEnter, this);
        this.node.on(Node.EventType.MOUSE_MOVE, this.onMouseMove, this);
        this.node.on(Node.EventType.MOUSE_LEAVE, this.onMouseLeave, this);
    }

    onMouseEnter() {
        tween(this.node)
        .to(0.1, {scale: v3(1.5, 1.5, 1)})
        .start();
    }

    onMouseMove(event: EventMouse) {
        const screenPos = event.getUILocation();
        const nodePos = this.node.getComponent(UITransform).convertToNodeSpaceAR(v3(screenPos.x, screenPos.y, 1));

        const angleX = this.remap(nodePos.x, -this.spriteWidth/2, this.spriteWidth/2, this.minAngle, this.maxAngle);
        const angleY = this.remap(nodePos.y, -this.spriteHeight/2, this.spriteHeight/2, this.minAngle, this.maxAngle);

        this.pass.setUniform(this.y_rot, angleX);
        this.pass.setUniform(this.x_rot, angleY);

        this.lastYRot = angleX;
        this.lastXRot = angleY;
    }

    onMouseLeave() {
        tween(this.node)
        .to(0.1, {scale: v3(1, 1, 1)})
        .start();
        
        // 缓慢改变材质属性
        tween({ angleVec2: v2(this.lastYRot, this.lastXRot) })
        .to(0.1, { angleVec2: Vec2.ZERO}, {
            onUpdate: (target) => {
                this.pass.setUniform(this.y_rot, target['angleVec2'].x);
                this.pass.setUniform(this.x_rot, target['angleVec2'].y);
            }
        })
        .call(()=>{
            this.lastYRot = 0;
            this.lastXRot = 0;
        })
        .start();
    }

    /**
     * 映射
     * @param num 当前值
     * @param sourceMin 原最小值
     * @param sourceMax 原最大值
     * @param targetMin 目标最小值
     * @param targetMax 目标最大值
     * @returns 映射后的目标值
     */
    remap(num: number ,sourceMin: number, sourceMax: number, targetMin: number = 0, targetMax: number = 1): number {
        
        if (num < sourceMin || num > sourceMax) {
            console.log(`Source Range Error: ${num} Not In [${sourceMin}, ${sourceMax}]`);
            return;
        }

        if (sourceMin > sourceMax) {
            console.log(`Source Range Error: sourceMin(${sourceMin}) > sourceMax(${sourceMax})`);
            return;
        }

        if (targetMin > targetMax) {
            console.log(`Target Range Error: targetMin(${targetMin}) > targetMax(${targetMax})`);
            return;
        }

        const sourceRange = sourceMax - sourceMin;
        const targetRange = targetMax - targetMin;

        if (sourceRange === 0) {
            console.log(`Source Range Warning: Source Range Is 0`);
            return targetMin;
        }

        const normalizedNum  = (num - sourceMin) / sourceRange;
        
        return targetMin + (normalizedNum * targetRange);
    }
}